package action

import (
	"fmt"
	"gitee.com/git_public/vfaith/builtin/shape"
	"gitee.com/git_public/vfaith/fount/frame"
	"gitee.com/git_public/vfaith/fount/handle"
	"gitee.com/git_public/vfaith/fount/middleware"
	"gitee.com/git_public/vfaith/sugar"
	"gitee.com/git_public/vfaith/toolkit/generate/dao"
	"gitee.com/git_public/vfaith/toolkit/generate/embed"
	"gitee.com/git_public/vfaith/toolkit/generate/model/bo"
	"gitee.com/git_public/vfaith/toolkit/generate/model/po"
	"go/format"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode"
)

// region 结构体定义&构造

func init() {
	frame.ActionRegistry[*GenAction]()
}

type GenAction struct {
	ColumnsDAO *dao.ColumnsDAO
}

func (action *GenAction) ActionInitialize() {
	action.ColumnsDAO = frame.GetDao[*dao.ColumnsDAO]()
	http.Handle("/gen/", middleware.UpdatePrefix("/gen/", "page/", http.FileServerFS(embed.FsGenerateWeb)))
	http.HandleFunc("/gen/form", action.FormPage)
	http.HandleFunc("/gen/exec", middleware.MidAuth(middleware.HttpContextFormNoError(action.ExecCodeGen)))
}

const (
	ACTION = "ACTION"
	BIZ    = "BIZ"
	DAO    = "DAO"
	DTO    = "DTO"
	MAPPER = "MAPPER"
	PO     = "PO"
	VO     = "VO"
	LIST   = "LIST"
	EDIT   = "EDIT"
	VIEW   = "VIEW"
)

// endregion

// ******************************************************************************
// ***************************** Defined Self START *****************************

// TODO:在这里写自定义方法

// ***************************** Defined Self END *******************************
// ******************************************************************************

// region Page

// FormPage 表单页面
func (action *GenAction) FormPage(w http.ResponseWriter, r *http.Request) {
	tmplPath := "page/gen.html"
	newTmpl, _ := template.New(filepath.Base(tmplPath)).Funcs(handle.GetTemplateFunc()).ParseFS(embed.FsGenerateWeb, tmplPath)
	_ = newTmpl.Execute(w, nil)
}

// ExecCodeGen 生成接口
func (action *GenAction) ExecCodeGen(handler *handle.FormHandler) {
	// HTTP参数获取
	var pathMap *shape.MapGnc[*bo.TmplInfo]
	var genModel = new(bo.GenModel)

	_, err := handler.InitParam(genModel)
	if handler.WriteErrorJSON(err) {
		return
	}

	// 查询获得表字段信息
	if genModel.GenType == 11 || genModel.GenType == 12 || genModel.GenType == 13 {
		columns, e1 := action.ColumnsDAO.GetColumns(genModel.TableName)
		if handler.WriteErrorJSON(e1) {
			return
		}
		genModel.Columns = columns
		genModel.ImportSlice = getImportSlice(columns) //循环日志列出查询到的字段

		if genModel.GenType == 11 {
			pathMap = getPathMap("po", "web", genModel)
		} else {
			pathMap = getPathMap("po", "api", genModel)
		}
	}

	if genModel.GenType == 21 || genModel.GenType == 22 {
		tableMap, e2 := action.ColumnsDAO.GetTableMap(genModel.ViewSql)
		if handler.WriteErrorJSON(e2) {
			return
		}
		genModel.TableMap = tableMap

		pathMap = getPathMap("vo", "api", genModel)
	}

	// 根据条件生成代码
	generateV1(pathMap, genModel)

	handler.ReturnText("生成结束")
}

// endregion

// region Method

func generateV1(pathMap *shape.MapGnc[*bo.TmplInfo], genModel *bo.GenModel) {
	pathAction, _ := pathMap.Get(ACTION)
	pathBIZ, _ := pathMap.Get(BIZ)
	pathDAO, _ := pathMap.Get(DAO)
	pathDTO, _ := pathMap.Get(DTO)
	pathMAPPER, _ := pathMap.Get(MAPPER)
	pathPO, _ := pathMap.Get(PO)
	pathVO, _ := pathMap.Get(VO)
	pathLIST, _ := pathMap.Get(LIST)
	pathEDIT, _ := pathMap.Get(EDIT)
	pathVIEW, _ := pathMap.Get(VIEW)

	writeToTemplate(pathAction, genModel)
	writeToTemplate(pathBIZ, genModel)
	writeToTemplate(pathDAO, genModel)
	writeToTemplate(pathDTO, genModel)
	writeToTemplate(pathMAPPER, genModel)

	switch genModel.GenType {
	case 12:
		writeToTemplate(pathPO, genModel)
		writeToTemplate(pathLIST, genModel)
		writeToTemplate(pathEDIT, genModel)
		writeToTemplate(pathVIEW, genModel)
	case 13:
		writeToTemplate(pathPO, genModel)
	case 21:
		writeToTemplate(pathVO, genModel)
		writeToTemplate(pathLIST, genModel)
	case 22:
		writeToTemplate(pathVO, genModel)
	default:
	}
}

func writeToTemplate(x1 *bo.TmplInfo, data *bo.GenModel) {

	tmplPath := x1.TmplPath
	genPath := x1.TargetPath

	var templateBytes []byte
	var err error

	// Read the template file
	if sugar.IsFolderExist("resources/tmpl/") {
		templateBytes, err = os.ReadFile("resources/" + tmplPath)
	} else {
		templateBytes, err = embed.FsGenerateTmpl.ReadFile(tmplPath)
	}

	if err != nil {
		log.Printf("Fail Generated 模板【%s】不存在", x1.TmplName)
		return
	}

	sugar.GetFolder(genPath)

	tmpl, err := template.New("struct").Funcs(getFuncMap()).Parse(string(templateBytes))
	if err != nil {
		log.Println(err)
		return
	}

	// Create the output file
	outputFile, err := os.Create(genPath)
	if err != nil {
		log.Println(err)
		return
	}
	defer outputFile.Close()

	// 执行模板并写入文件
	var output strings.Builder
	err = tmpl.Execute(&output, data)
	if err != nil {
		log.Println(err)
		return
	}

	// 替换占位符
	finalOutput := strings.ReplaceAll(output.String(), "{|{|", "{{")
	finalOutput = strings.ReplaceAll(finalOutput, "}|}|", "}}")

	if strings.Index(genPath, ".html") > 0 {
		_, err = outputFile.WriteString(finalOutput)
		if err != nil {
			log.Println(err)
			return
		}

		log.Printf("Successfully Generated File 【%s】.", x1.TmplName)
		return
	}

	// 格式化代码
	formattedOutput, err := format.Source([]byte(finalOutput))
	if err != nil {
		log.Println("Error formatting source:", err)
		formattedOutput = []byte(finalOutput) // 格式化失败时使用未格式化的代码
	}

	_, err = outputFile.Write(formattedOutput)
	if err != nil {
		log.Println(err)
		return
	}

	log.Printf("Successfully Generated and Formatted File 【%s】.", x1.TmplName)
}

func getPathMap(entityType string, publicType string, genParam *bo.GenModel) *shape.MapGnc[*bo.TmplInfo] {

	var tmplInfo = shape.NewMapGnc[*bo.TmplInfo]()
	var formatTmplPath = "tmpl/tmpl_" + entityType + "_" + publicType + "/template_%s.tmpl"

	//********************************************************************************

	//输出路径 - Go文件-Public - 【public/dashboard/api/模块名/功能名/文件名_功能后缀.go】
	formatGenGoPublicPath := "public/dashboard/" + publicType + "/%s/%s/%s_%s.go"

	actionTmplPath := fmt.Sprintf(formatTmplPath, "go_action")
	actionGenPath := fmt.Sprintf(formatGenGoPublicPath, genParam.ModuleName, "controller", genParam.FileName, "action")
	tmplInfo.Add(ACTION, bo.NewTmplInfo(ACTION, actionTmplPath, actionGenPath))

	reqTmplPath := fmt.Sprintf(formatTmplPath, "go_dto")
	reqGenPath := fmt.Sprintf(formatGenGoPublicPath, genParam.ModuleName, "dto", genParam.FileName, "dto")
	tmplInfo.Add(DTO, bo.NewTmplInfo(DTO, reqTmplPath, reqGenPath))

	//********************************************************************************

	//输出路径 - Go文件-Internal - 【internal/模块名/功能名/文件名_功能后缀.go】
	formatGenGoInternalPath := "internal/%s/%s/%s_%s.go"

	bizTmplPath := fmt.Sprintf(formatTmplPath, "go_biz")
	bizGenPath := fmt.Sprintf(formatGenGoInternalPath, genParam.ModuleName, "biz", genParam.FileName, "biz")
	tmplInfo.Add(BIZ, bo.NewTmplInfo(BIZ, bizTmplPath, bizGenPath))

	daoTmplPath := fmt.Sprintf(formatTmplPath, "go_dao")
	daoGenPath := fmt.Sprintf(formatGenGoInternalPath, genParam.ModuleName, "dao", genParam.FileName, "dao")
	tmplInfo.Add(DAO, bo.NewTmplInfo(DAO, daoTmplPath, daoGenPath))

	mapperTmplPath := fmt.Sprintf(formatTmplPath, "go_mapper")
	mapperGenPath := fmt.Sprintf(formatGenGoInternalPath, genParam.ModuleName, "mapper", genParam.FileName, "mapper")
	tmplInfo.Add(MAPPER, bo.NewTmplInfo(MAPPER, mapperTmplPath, mapperGenPath))

	entityPoTmplPath := fmt.Sprintf(formatTmplPath, "go_po")
	entityPoGenPath := fmt.Sprintf(formatGenGoInternalPath, genParam.ModuleName, "model/po", genParam.FileName, "po")
	tmplInfo.Add(PO, bo.NewTmplInfo(PO, entityPoTmplPath, entityPoGenPath))

	entityVoTmplPath := fmt.Sprintf(formatTmplPath, "go_vo")
	entityVoGenPath := fmt.Sprintf(formatGenGoInternalPath, genParam.ModuleName, "model/vo", genParam.FileName, "vo")
	tmplInfo.Add(VO, bo.NewTmplInfo(VO, entityVoTmplPath, entityVoGenPath))

	//********************************************************************************

	//输出路径 - HTML文件 - 【resources/template/dashboard/模块名/文件名/功能名.html】
	formatGenHtmlPath := "resources/static/dashboard/pages/%s/%s/%s.html"

	listTmplPath := fmt.Sprintf(formatTmplPath, "html_list")
	listGenPath := fmt.Sprintf(formatGenHtmlPath, genParam.ModuleName, genParam.FileName, "list")
	tmplInfo.Add(LIST, bo.NewTmplInfo(LIST, listTmplPath, listGenPath))

	editTmplPath := fmt.Sprintf(formatTmplPath, "html_edit")
	editGenPath := fmt.Sprintf(formatGenHtmlPath, genParam.ModuleName, genParam.FileName, "edit")
	tmplInfo.Add(EDIT, bo.NewTmplInfo(EDIT, editTmplPath, editGenPath))

	viewTmplPath := fmt.Sprintf(formatTmplPath, "html_view")
	viewGenPath := fmt.Sprintf(formatGenHtmlPath, genParam.ModuleName, genParam.FileName, "view")
	tmplInfo.Add(VIEW, bo.NewTmplInfo(VIEW, viewTmplPath, viewGenPath))

	//********************************************************************************

	return tmplInfo
}

func getImportSlice(columns []po.Column) []string {

	importSlice := make([]string, 0)
	for _, column := range columns {
		//log.Printf("Index: %d, Value: %s", i, column.Name)
		if column.Type == "datetime" && !strings.Contains(strings.Join(importSlice, ","), "gitee.com/git_public/vfaith/builtin/shape") {
			importSlice = append(importSlice, "gitee.com/git_public/vfaith/builtin/shape")
		}
		if column.Type == "date" && !strings.Contains(strings.Join(importSlice, ","), "gitee.com/git_public/vfaith/builtin/shape") {
			importSlice = append(importSlice, "gitee.com/git_public/vfaith/builtin/shape")
		}
		if column.Type == "decimal" && !strings.Contains(strings.Join(importSlice, ","), "github.com/shopspring/decimal") {
			importSlice = append(importSlice, "github.com/shopspring/decimal")
		}
	}

	return importSlice
}

func getFuncMap() template.FuncMap {
	return template.FuncMap{
		"add": func(a, b int) int {
			return a + b
		},
		"mod": func(i, j int) int {
			return i % j
		},
		"title": func(str string) string {
			return titleCase(str)
		},
		"subString": func(s string) string {
			parts := strings.Split(s, ":")
			return parts[0]
		},
		"goType": func(sqlType string) string {
			switch sqlType {
			case "tinyint", "int", "bigint":
				return "int"
			case "varchar", "text":
				return "string"
			case "date", "datetime":
				return "shape.CusTime"
			case "decimal":
				return "decimal.Decimal"
			default:
				return "interface{}"
			}
		},
		"swagType": func(sqlType string) string {
			switch sqlType {
			case "date", "datetime":
				return " swaggertype:\"string\""
			case "int", "bigint":
				return " swaggertype:\"integer\""
			default:
				return ""
			}
		},
	}
}

func titleCase(s string) string {
	var result []rune
	capitalizeNext := true

	for _, r := range s {
		if r == '_' {
			capitalizeNext = true
		} else {
			if capitalizeNext {
				result = append(result, unicode.ToUpper(r))
				capitalizeNext = false
			} else {
				result = append(result, r)
			}
		}
	}

	return string(result)
}

/*func DevMid(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		env := os.Getenv("GO_APP_ENV")
		if env != "dev" {
			handle.ReturnHTML(w, "只能在开发环境使用")
		} else {
			http.Redirect(w, r, "/toolkit/generate/page/gen.html", http.StatusFound)
		}

		next.ServeHTTP(w, r)
	}
}*/

// CodeGenExample 代码生成文件示例
func CodeGenExample() {
	outputFile, err := os.Create("public/test.go")
	formattedOutput, err := format.Source([]byte("package public"))
	_, err = outputFile.Write(formattedOutput)
	if err != nil {
		log.Println(err)
		return
	}
}

// endregion
